#include "xMappingNode.h"
#include "xNodeType.h"
#include "xNodeType_WOP.h"
#include "vmLibrary.h"
#include "xParameterPack.h"
#include "all_entries.h"
#include "xOutMappingPort.h"
#include "xInMappingPort.h"

/// ==================  template from_string ====================== 
/// function for converting c++ string to any type user gives
/// ===============================================================
template <class T>
bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&))
{
	std::istringstream iss(s);
	return !(iss >> f >> t).fail();
}

using namespace std; 
void xNodeType_WOP::setupPorts() {

	assert(_parent != NULL);
	_parent->_outports.push_back(new xOutMappingPort("wop", _parent));

}

xParameter xNodeType_WOP::toXParameter(string type, xOutMappingPort * linked_op) {
	// OK, this node is essentially a wop mapping ndoe
	// when this node is connect to a parent P
	// P can ask for its wop entry, integer pointer, integer value, etc.
	// we should return the correct content according to toType 
	// usually a WOP node only have one output port, so we ignore the linked_op 
	xParameter xp ;
	if (type == "wop") {
		xp.type = T_WOP ;
		xp.wop_nodetype = this ; // we save this pointer so that a gate wop can trace back
		xp.val_wop = _wop ;
	} else if (type == "varname") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getName();
	} else if (type == "int*") {
		xp.type = TP_INT;
		// FIXME dynamic cast is very slow and unsafe
		if( WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop) ) {
			xp.val_int_ptr = iwop->getIntValAddr();
		} else if ( WOP_Double_Proxy * dwop = dynamic_cast< WOP_Double_Proxy * >( _wop ) ) {	// if dynamic cast fail, try cast to double or float
			xp.val_int_ptr = dwop->getIntValAddr();
		} else if( WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop) ) {
			xp.val_int_ptr = fwop->getIntValAddr();
		} else {
			// TODO throw an exception
			assert( !"cant not provide this variable type casting" );
		}
		//xp.original = iwop->getVal();
	} /* else if (type == "int") {
		xp.type = T_INT ;
		WOP_Int_Entry * iwop = dynamic_cast<WOP_Int_Entry*>(_wop);
		assert(iwop != NULL) ;
		string val = iwop->getVal();
		int value ;
		assert(from_string<int>(value, std::string(val), std::boolalpha));
		xp.val_int = value ;
	}*/ else if (type == "bool*") {
		xp.type = TP_BOOL ;
		WOP_Bool_Proxy * bwop = dynamic_cast<WOP_Bool_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_bool_ptr = bwop->getBoolValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "float*") {
		// an inport of type float* may be connected to double*, and vice versa 
		// so we need to check the type of an wop first
		xp.type = TP_FLOAT ;
		WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
		if (fwop == NULL){
			WOP_Double_Proxy * dwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
			xp.val_float_ptr = dwop->getFloatValAddr();

			if ( dwop == NULL ){
				WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
				xp.val_float_ptr = iwop->getFloatValAddr();
				assert(iwop != NULL) ;
			}
		}else {
			xp.val_float_ptr = fwop->getFloatValAddr();
		}
		//xp.original = bwop->getVal();
	} else if (type == "double*") {
		xp.type = TP_DOUBLE ;
		WOP_Double_Proxy * dwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
		//if dynamic cast fail, try cast to int or float
		if (dwop == NULL){
			WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
			xp.val_double_ptr = fwop->getDoubleValAddr();
			if ( fwop == NULL ){
				WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
				xp.val_double_ptr = iwop->getDoubleValAddr();
				assert(iwop != NULL) ;
			}
		}else{
			xp.val_double_ptr = dwop->getDoubleValAddr();
		}
	} else if (type == "char*") {
		xp.type = TP_CHAR ;
		WOP_Char_Proxy * bwop = dynamic_cast<WOP_Char_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_char_ptr = bwop->getCharValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "ints") { 
		// this is the case where a composite VM asking for dimension
		assert(dynamic_cast<WOP_Array_Entry*>(_wop) != NULL) ;
		WOP_Array_Entry * ary = (WOP_Array_Entry*) _wop ;
		xp.type = T_INTS ;
		xp.val_ints = ary->getDimensionRange();
	} else if (type == "string") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getVal();
		//xp.original = _wop->getVal();
	} else if (type == "string*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
		//xp.original = _wop->getVal();
	} else if (type == "tostring*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
		//xp.original = _wop->getVal();
	} else {
		// TODO throw an exception
		assert( !"invalid type" );
	}
	return xp ;
}

string xNodeType_WOP::toMDString( MDSType type ) {
	ostringstream MDSstr ;

	if ( type == xNodeType::NormalMDSFormat){
		MDSstr <<  " node " << _parent->getSerialNo() << " wop " << _wop->getVarType() << " " << _wop->getName() << endl ; 
	} else if ( type == xNodeType::ParameterizedMDSFormat ){
		WOP_Array_Entry* wop = dynamic_cast<WOP_Array_Entry*>(_wop);
		assert(wop);

		std::string type = wop->getArrayElementType();
		size_t index = type.find("[");
		type = type.substr(0, index);

		MDSstr << "ADD_NODE " <<"node " << "SN" << _parent->getSerialNo() << " wop " << type << " " << wop->getGenericName() << endl ; 
	} else {
		assert(false);
	}
	
	string mds = MDSstr.str();
	return mds ;
}

int  xNodeType_WOP::parseMDString(vector<string>& tokens, int start_index) {
	int i = start_index ;
	assert(tokens[i] == "node");
	string serialno = tokens[i+1] ;
	assert(tokens[i+2] == "wop");
	string nodetype = tokens[i+3] ; 
	string wopname = tokens[i+4] ; 

	return 5 ;
}
string xNodeType_WOP::valid_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
"int", "int*", 
"int", "float*", // the integer value is casted to a float 
"int", "double*",
"bool", "bool*",
"bool", "int*",   // the boolean will be translated into 0 and 1 and fed to integer
"float","int*",   // the float value is truncated to integer, information could be lost
"float","float*",
"float","double*",
"double","int*",
"double","float*",
"double","double*",
"char","char*",
"char","int*",  // we allow a integer to get the byte data from a char
"string", "string*",
"ref","wop",
"dontcare", "tostring*",
"dontcare", "varname",
"dontcare","wop"
};

bool xNodeType_WOP::verify_rhs_link(xOutMappingPort *left, xInMappingPort *right, xNodeType* rightnode) {

	string wop_type = _wop->getTypeStr() ;
	return check_valid_pair(valid_pair, sizeof(valid_pair)/(sizeof(string)*2),
							wop_type, right->getType());
}
bool xNodeType_WOP::verify_lhs_link(xOutMappingPort *left, xNodeType* leftnode, xInMappingPort *right) {
	return true ; // no node should connect from the left hand side
}

